//
// Created by 吴喆 on 2020/3/4.
//

#include "ResourceManager.h"

std::unordered_map<std::string, Shader *> ResourceManager::shaders = std::unordered_map<std::string, Shader *>(
        10);/* NOLINT */
std::unordered_map<std::string, Texture2D *> ResourceManager::textures = std::unordered_map<std::string, Texture2D *>(
        10);/* NOLINT */

Shader *ResourceManager::loadShader(const GLchar *vShaderFile,
                                    const GLchar *fShaderFile,
                                    const GLchar *gShaderFile,
                                    const std::string name) {
    std::string vertexCode;
    std::string fragmentCode;
    std::string geometryCode;
    std::stringstream vertexStream, fragmentStream;

    try {
        std::ifstream vFile(vShaderFile);
        vertexStream << vFile.rdbuf();
        vertexCode = vertexStream.str();
        std::ifstream fFile(fShaderFile);
        fragmentStream << fFile.rdbuf();
        fragmentCode = fragmentStream.str();
        vFile.close();
        fFile.close();
        if (gShaderFile != nullptr) {
            std::ifstream geometryShaderFile(gShaderFile);
            std::stringstream gShaderStream;
            gShaderStream << geometryShaderFile.rdbuf();
            geometryShaderFile.close();
            geometryCode = gShaderStream.str();
        }
    } catch (std::exception &e) {
        Console::ERR("ERROR::SHADER: Failed to read shader files");
    }
    auto *shader = new Shader{};
    std::string tmp("PREPARE TO COMPILE SHADER:");
    tmp.append(vShaderFile).append(fShaderFile);
    Console::INFO(tmp);
    shader->compile(vertexCode.c_str(), fragmentCode.c_str(), gShaderFile != nullptr ? geometryCode.c_str() : nullptr);

    // 存放在map中
    shaders[name] = shader;
    return shader;
}

Shader *ResourceManager::getShader(const std::string name) {
    return shaders[name];
}

Texture2D *ResourceManager::loadTexture(const GLchar *file, GLboolean alpha, IMG_TYPE type, std::string &name) {
    auto *texture = new Texture2D{};
//    if (alpha) {
//        texture.internalFormat = GL_RGBA;
//        texture.imageFormat = GL_RGBA;
//    }
    // Load image
    int width, height, nrComponents;
    unsigned char *image = nullptr;
#if __APPLE__
    stbi_set_flip_vertically_on_load(true);
    image = stbi_load(file, &width, &height, &nrComponents, 0);
#else
    image = SOIL_load_image(file, &width, &height, &nrComponents, 0);
#endif
    if (image != nullptr) {
        GLenum format = 0;
        if (nrComponents == 1)
            format = GL_RED;
        else if (nrComponents == 3)
            format = GL_RGB;
        else if (nrComponents == 4)
            format = GL_RGBA;
        texture->imageFormat = format;
        texture->internalFormat = format;
        texture->generate(width, height, image);
        std::string res = "successfully load texture:";
        res.append(name);
        Console::INFO(res);
    } else {
        std::string err = "CAN NOT LOAD Image:";
        err.append(name);
        Console::ERR(err);
    }
#if __APPLE__
    stbi_image_free(image);
#else
    SOIL_free_image_data(image);
#endif
    textures[name] = texture;
    return texture;
}
Texture2D *ResourceManager::getTexture(const std::string &name) {
    Texture2D *texture2D = textures[name];
    if (texture2D == nullptr) {
        Console::ERR("CAN NOT FOUND TEXTURE:" + name);
    }
    return texture2D;
}

void ResourceManager::clear() {
    for (const auto &iter : shaders) {
        glDeleteProgram(iter.second->id);
    }
    // (Properly) delete all textures
    for (const auto &iter : textures)
        glDeleteTextures(1, &iter.second->id);
}

// DT_UNKNOWN，未知的类型
// DT_REG，普通文件
// DT_DIR，普通目录
// DT_FIFO，命名管道或FIFO
// DT_SOCK，本地套接口
// DT_CHR，字符设备文件
// DT_BLK，块设备文件
void ResourceManager::loadTextureFromDir() {
    std::string path = ConfigUtil::getProp(ConfigConst::MATERIAL_PATH);

#if __APPLE__
    dirent *dirp;
    dirent *subDirp;

    DIR *dir = opendir(path.c_str());
    DIR *subDir;

    while ((dirp = readdir(dir)) != nullptr) {
        // 文件夹
        if (dirp->d_type == DT_DIR) {
            if (std::strcmp(dirp->d_name, ".") != 0
                && std::strcmp(dirp->d_name, "..") != 0
                && std::strcmp(dirp->d_name, ".DS_Store") != 0) {
                std::string subPath = path + "/" + dirp->d_name;
                subDir = opendir(subPath.c_str());
                while ((subDirp = readdir(subDir)) != nullptr) {

                    if (subDirp->d_type == DT_REG) {
                        std::string fileName = subPath + "/" + subDirp->d_name;
                        std::ifstream fStream;
                        fStream.open(fileName);
                        IMG_TYPE res = FileUtil::isImage(fStream);
                        if (IMG_TYPE::UNKNOWN != res) {
                            std::string tmp(dirp->d_name);
                            tmp.append(".").append(subDirp->d_name);
                            // load texture
                            loadTexture(fileName.c_str(), GL_TRUE, res, tmp);
                        }
                    }

                }
                closedir(subDir);
            }
        }
    }
    closedir(dir);
#endif
}
